﻿using System;
using System.Collections;
using System.IO;
using System.Web;
using EPocalipse.IFilter;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using umbraco.BusinessLogic;
using umbraco.cms.businesslogic;
using umbraco.cms.businesslogic.media;
using umbraco.cms.businesslogic.property;
using umbraco.cms.businesslogic.web;

namespace UmbSearch2.Search
{
	public class Indexer
	{
		private static string indexDirectory = "";

		public static string IndexDirectory
		{
			get
			{
				try
				{
					if (indexDirectory == "")
					{
						indexDirectory = HttpContext.Current.Server.MapPath("/usercontrols/UmbSearch2/UmbSearch2IndexDontDelete");
						// check if directory exists
						Directory.CreateDirectory(indexDirectory);
					}
					return indexDirectory;
				}
				catch
				{
					return "";
				}
			}
		}

		/// <summary>
		/// Method for accesing the Lucene indexwriter in the internal search
		/// </summary>
		/// <param name="ForceRecreation">If set to true, a new index is created</param>
		/// <returns>The lucene indexwriter</returns>
		public static IndexWriter GetIndexWriter(bool ForceRecreation)
		{
			if (!ForceRecreation && System.IO.Directory.Exists(IndexDirectory) &&
			    new System.IO.DirectoryInfo(IndexDirectory).GetFiles().Length > 1)
				return new IndexWriter(IndexDirectory, new StandardAnalyzer(), false);
			else
			{
				IndexWriter iw = new IndexWriter(IndexDirectory, new StandardAnalyzer(), true);
				return iw;
			}
		}

		/// <summary>
		/// Method for accessing the Lucene indexreader
		/// </summary>
		/// <returns></returns>
		public static IndexReader GetIndexReader()
		{
			return IndexReader.Open(IndexDirectory);
		}

		/// <summary>
		/// Indexes the document
		/// </summary>
		/// <param name="node"></param>
		public static void Index(umbraco.cms.businesslogic.web.Document node)
		{
			// remove node if exists
			RemoveNode(node.Id);

			// nodes that are marked hide from searching shouldn't be indexed
			if (node.getProperty("umbracoSearchHide") != null)
			{
				int v = 0;
				int.TryParse(node.getProperty("umbracoSearchHide").Value.ToString(), out v);
				if (v == 1)
					return;
			}

			Hashtable fields = new Hashtable();
			foreach (Property p in node.getProperties)
			{
				fields.Add(p.PropertyType.Alias, p.Value.ToString());
			}

			fields.Add(FieldName.DocumentType.ToString(), node.ContentType.Id);

			AddNodeToIndex(node, fields, false);
		}

		/// <summary>
		/// Indexes the media item
		/// </summary>
		/// <param name="node"></param>
		public static void Index(Media node)
		{
			// remove node if exists
			RemoveNode(node.Id);

			// nodes that are marked hide from searching shouldn't be indexed
			if (node.getProperty("umbracoSearchHide") != null)
			{
				int v = 0;
				int.TryParse(node.getProperty("umbracoSearchHide").Value.ToString(), out v);
				if (v == 1)
					return;
			}

			string extension = "";
			if (node.getProperties.Length > 0)
			{
				extension = node.getProperty("umbracoExtension").Value.ToString();
			}

			if (extension != "txt" && extension != "pdf" && extension != "doc" && extension != "docx")
				return;

			string filePath = node.getProperty("umbracoFile").Value.ToString();
			string fileContent = "";

			try
			{
				TextReader reader = new FilterReader(HttpContext.Current.Server.MapPath(filePath));
				fileContent = reader.ReadToEnd();
			}
			catch (Exception ee)
			{
				umbraco.BusinessLogic.Log.Add(umbraco.BusinessLogic.LogTypes.Error, umbraco.BusinessLogic.User.GetUser(0), node.Id, "Error indexing node: (" + ee.ToString() + ")");
			}

			Hashtable fields = new Hashtable();
			fields.Add(FieldName.Extension, extension);
			fields.Add(FieldName.FilePath, filePath);
			fields.Add(FieldName.FileContent, fileContent);

			AddNodeToIndex(node, fields, true);
		}

		private static void AddNodeToIndex(CMSNode node, Hashtable fields, bool IsMediaItem)
		{
			Lucene.Net.Documents.Document d = new Lucene.Net.Documents.Document();
			d.Add(new Field(FieldName.Id.ToString(), node.Id.ToString(), Field.Store.YES, Field.Index.UN_TOKENIZED, Field.TermVector.NO));
			d.Add(new Field(FieldName.Title.ToString(), node.Text, Field.Store.YES, Field.Index.TOKENIZED, Field.TermVector.YES));
			d.Add(new Field(FieldName.CreateDate.ToString(), convertDate(node.CreateDateTime), Field.Store.YES, Field.Index.UN_TOKENIZED, Field.TermVector.NO));
			
			if(IsMediaItem)
			{
				d.Add(new Field(FieldName.ObjectType.ToString(), Media._objectType.ToString(), Field.Store.YES, Field.Index.UN_TOKENIZED, Field.TermVector.NO));
			}
			else
			{
				d.Add(new Field(FieldName.ObjectType.ToString(), umbraco.cms.businesslogic.web.Document._objectType.ToString(), Field.Store.YES, Field.Index.UN_TOKENIZED, Field.TermVector.NO));
			}
            
			// Add all fields
			if (fields != null)
			{
				IDictionaryEnumerator ide = fields.GetEnumerator();
				while (ide.MoveNext())
				{
					d.Add(new Field(ide.Key.ToString(), ide.Value.ToString(), Field.Store.YES, Field.Index.TOKENIZED, Field.TermVector.YES));

				}
			}

			IndexWriter writer = GetIndexWriter(false);
			try
			{
				writer.AddDocument(d);
				writer.Optimize();
				writer.Close();
			}
			catch (Exception ee)
			{
				umbraco.BusinessLogic.Log.Add(umbraco.BusinessLogic.LogTypes.Error, umbraco.BusinessLogic.User.GetUser(0), node.Id, "Error indexing node: (" + ee.ToString() + ")");
			}
			finally
			{
				writer.Close();
			}
		}
        
		/// <summary>
		/// Removes the node from the index.
		/// </summary>
		/// <param name="Id">The id.</param>
		public static void RemoveNode(int Id)
		{
			IndexReader ir = null;
			try
			{
				ir = GetIndexReader();
				ir.DeleteDocuments(new Term("Id", Id.ToString()));
			}
			catch (Exception ee)
			{
				umbraco.BusinessLogic.Log.Add(
					umbraco.BusinessLogic.LogTypes.Error,
					umbraco.BusinessLogic.User.GetUser(0),
					Id,
					"Error removing node from umbraco index: '" + ee.ToString() + "'");
			}
			finally
			{
				if (ir != null)
					ir.Close();
			}
		}

		private static string convertDate(DateTime Date)
		{
			try
			{
				string thisYear = Date.Year.ToString();
				if (thisYear.Length == 1)
					thisYear = "0" + thisYear;
				string thisMonth = Date.Month.ToString();
				if (thisMonth.Length == 1)
					thisMonth = "0" + thisMonth;
				string thisDay = Date.Day.ToString();
				if (thisDay.Length == 1)
					thisDay = "0" + thisDay;

				return thisYear + thisMonth + thisDay;
			}
			catch
			{
				return "";
			}
		}

		/// <summary>
		/// Method for reindexing data in all documents and media items of umbraco, this is a performaceheavy invocation and should be
		/// used with care!
		/// </summary>
		public static void ReIndex()
		{
			HttpContext currentHttpContext = HttpContext.Current;

			if (currentHttpContext.Application["UmbSearch2Indexing"] != null && currentHttpContext.Application["UmbSearch2Indexing"].ToString() == "true")
				return;

			currentHttpContext.Application["UmbSearch2Indexing"] = "true";

			int nodeId = 0;

			try
			{
				// removes all the index files
				IndexWriter w = GetIndexWriter(true);
				w.Close();

				Guid[] documents = CMSNode.getAllUniquesFromObjectType(umbraco.cms.businesslogic.web.Document._objectType);

				foreach (Guid g in documents)
				{
					umbraco.cms.businesslogic.web.Document d = new umbraco.cms.businesslogic.web.Document(g);
					nodeId = d.Id;
					if(d.Published)
						Index(d);
				}

				Guid[] mediaItems = CMSNode.getAllUniquesFromObjectType(Media._objectType);

				foreach (Guid g in mediaItems)
				{
					Media m = new Media(g);
					nodeId = m.Id;
					
					if(m.ContentType.Alias == "File")
						Index(m);
				}
			}
			catch (Exception ex)	
			{
				Log.Add(LogTypes.Error, User.GetCurrent(), nodeId, string.Format("Error creating index for node: {0}", ex));
			}
			finally
			{
				currentHttpContext.Application["UmbSearch2Indexing"] = "done";
			}
		}


		/// <summary>
		/// Determines whether this instance is reindexing.
		/// </summary>
		/// <returns>
		/// 	<c>true</c> if this instance is reindexing; otherwise, <c>false</c>.
		/// </returns>
		public static bool IsReindexing()
		{
			if (System.Web.HttpContext.Current != null)
				if (System.Web.HttpContext.Current.Application["UmbSearch2Indexing"] != null && System.Web.HttpContext.Current.Application["UmbSearch2Indexing"].ToString() == "true")
					return true;

			return false;
		}
	}
}